package com.mdinic.blogovi.service;

import java.awt.Dimension;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.PostConstruct;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.fit.cssbox.demo.ImageRenderer;
import org.fit.cssbox.demo.Resizer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.util.HtmlUtils;
import org.xml.sax.SAXException;

import com.mdinic.blogovi.model.Blog;
import com.mdinic.blogovi.model.BlogPost;
import com.mdinic.blogovi.model.BlogType;
import com.mdinic.blogovi.model.Comment;
import com.mdinic.blogovi.model.Fav;
import com.mdinic.blogovi.model.Person;
import com.mdinic.blogovi.model.ReadShare;
import com.mdinic.blogovi.model.Share;
import com.mdinic.blogovi.model.Vote;
import com.mdinic.blogovi.service.model.BlogPostResult;
import com.mdinic.blogovi.service.model.BlogPostResultType;

@Service
class BlogPostServiceImpl implements BlogPostService {

    private static final Logger LOG = Logger.getLogger(BlogPostService.class);

    private final ExecutorService threadPool = Executors.newFixedThreadPool(5);

    public static String THUMB_PATH = "thumb/";

    @Value("${image.upload.path}")
    private String uploadPhotoPath;

    private String uploadPhotoPathThumb;

    @PostConstruct
    protected void init() {
        // public static String PHOTO_PATH = UPLOAD_PATH + "p/";
        // public static String SMALL_PHOTO_PATH = UPLOAD_PATH + "sp/";
        // new File(PHOTO_PATH).mkdirs();
        // new File(SMALL_PHOTO_PATH).mkdirs();
        uploadPhotoPathThumb = uploadPhotoPath + THUMB_PATH;
        new File(uploadPhotoPathThumb).mkdirs();
    }

    @Override
    public BlogPost vote(Person person, BlogPost blogPost, boolean voteUp) {
        LOG.debug("Vote " + blogPost + " " + voteUp);
        List<Vote> resultList = Vote.findVotesByPostAndPerson(blogPost, person).getResultList();
        BlogPost post = BlogPost.findBlogPost(blogPost.getId());
        if (resultList.isEmpty()) {
            Vote vote = new Vote(person, post, voteUp);
            vote.persist();

            if (vote.getUp()) {
                post.setVoteUp(post.getVoteUp() + 1);
                post.merge();
            } else {
                post.setVoteDown(post.getVoteDown() + 1);
                post.merge();
            }
        } else {
            Vote vote = resultList.get(0);
            if (vote.getUp()) {
                if (!voteUp) {
                    vote.setUp(false);
                    vote.merge();
                    post.setVoteUp(post.getVoteUp() - 1);
                    post.setVoteDown(post.getVoteDown() + 1);
                    post.merge();
                }
            } else {
                if (voteUp) {
                    vote.setUp(true);
                    vote.merge();
                    post.setVoteUp(post.getVoteUp() + 1);
                    post.setVoteDown(post.getVoteDown() - 1);
                    post.merge();
                }
            }
        }
        return post;
    }

    @Override
    public Boolean fav(Person person, BlogPost blogPost) {
        LOG.debug("Fav " + blogPost);
        List<Fav> resultList = Fav.findFavsByPostAndPerson(blogPost, person).getResultList();
        if (resultList.isEmpty()) {
            Fav fav = new Fav();
            fav.setPerson(person);
            fav.setPost(blogPost);
            fav.persist();
            return true;
        } else {
            resultList.get(0).remove();
            return false;
        }
    }

    @Override
    public BlogPostResult post(BlogPost blogPost) {

        BlogPostResult result = new BlogPostResult();

        List<BlogPost> byUrl = BlogPost.findBlogPostsByUrlEquals(blogPost.getUrl()).getResultList();
        Blog blog = null;
        if (byUrl.isEmpty()) {

            String url = blogPost.getUrl();
            if (!url.startsWith("http://") && !url.startsWith("https://")) {

                result.setType(BlogPostResultType.URL_WITHOUT_HTTP);
                return result;
            } else {
                try {
                    String domainName = getDomainName(url);
                    List<Blog> resultList = Blog.findBlogsByUrlEquals(domainName).getResultList();
                    if (!resultList.isEmpty()) {
                        blog = resultList.get(0);
                    }
                } catch (URISyntaxException e) {
                    LOG.debug("Error reading url " + blogPost + " url " + blogPost.getUrl(), e);
                    result.setType(BlogPostResultType.URL_INVLID_FORMAT);
                    return result;
                }

                int fromIndex = url.indexOf("://");
                int toIndex = url.indexOf("/", fromIndex + 3);
                String blogUrl = url.substring(0, toIndex);
                List<Blog> resultList = Blog.findBlogsByUrlEquals(blogUrl).getResultList();
                if (resultList.isEmpty()) {

                    blog = new Blog();
                    blog.setUrl(blogUrl);
                    blog.setAutoApprove(false);
                    blog.setBlacklisted(false);
                    blog.persist();
                } else {
                    blog = resultList.get(0);
                }
            }

            if (blog.getBlacklisted()) {
                result.setType(BlogPostResultType.BLACKLISTED);
                return result;
            }

            blogPost.setBlog(blog);
            blogPost.setPosted(new Date());
            blogPost.setApproved(blog.getAutoApprove() || blogPost.getPerson().getAutoApprove());
            blogPost.setVoteUp(0);
            blogPost.setVoteDown(0);
            blogPost.setComment(0);
            blogPost.setView(0);
            blogPost.setClick(0);

            threadPool.execute(new BlogPostThumbMaker(blogPost, uploadPhotoPathThumb));

            result.setType(BlogPostResultType.OK);
        } else {
            BlogPost post = byUrl.get(0);
            result.setPostId(post.getId());
            result.setType(BlogPostResultType.EXISTING);
        }
        return result;
    }

    @Override
    public List<BlogType> getBlogTypes() {
        return BlogType.findAllBlogTypes();
    }

    private class BlogPostThumbMaker implements Runnable {

        private final String dir;
        private final BlogPost blogPost;

        public BlogPostThumbMaker(BlogPost blogPost, String dir) {
            super();
            this.blogPost = blogPost;
            this.dir = dir;
        }

        @Override
        public void run() {
            FileOutputStream os = null;
            String fileName = UUID.randomUUID().toString().replace("-", "") + ".png";
            try {
                System.out.println("path: " + dir + fileName);
                os = new FileOutputStream(dir + fileName);
                ImageRenderer r = new ImageRenderer();
                r.renderURL(blogPost.getUrl(), os, ImageRenderer.TYPE_PNG, Resizer.AVERAGE, new Dimension(200, 128));

                blogPost.setThumb(fileName);
            } catch (FileNotFoundException e) {
                LOG.error("Output file cound not be created " + fileName, e);
            } catch (IOException e) {
                LOG.error("Error reading html or writing image to disk", e);
            } catch (SAXException e) {
                LOG.error("Error pasing html on " + blogPost.getUrl(), e);
            } finally {
                if (os != null) {
                    try {
                        os.close();
                    } catch (IOException e) {
                        LOG.error("Error while closing stream", e);
                    }
                }
                blogPost.persist();
            }
        }
    }

    private String getDomainName(String url) throws URISyntaxException {
        URI uri = new URI(url);
        String domain = uri.getHost();
        return domain.startsWith("www.") ? domain.substring(4) : domain;
    }

    @Override
    public BlogPost addComment(Comment comment) {
        comment.setDate(new Date());
        comment.persist();
        comment.flush();
        BlogPost post = BlogPost.findBlogPost(comment.getPost().getId());
        post.incComment();
        post.merge();
        post.flush();
        return post;
    }

    @Override
    public String getTitle(String sUrl) {
        try {
            URL url = new URL(sUrl);
            URLConnection urlConnection = url.openConnection();
            urlConnection.setRequestProperty("User-Agent",
                    "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2");
            BufferedReader dis = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
            StringBuilder html = new StringBuilder();

            String inputLine;
            while ((inputLine = dis.readLine()) != null) {
                html.append(inputLine);
            }
            dis.close();

            String sHtml = html.toString().replaceAll("\\s+", " ");
            Pattern p = Pattern.compile("<title>(.*?)</title>");
            Matcher m = p.matcher(sHtml);
            if (m.find()) {
                String titleRow = m.group(1);
                return HtmlUtils.htmlUnescape(titleRow);
            }
        } catch (RuntimeException e) {
            LOG.error("Error while getting title", e);
            e.printStackTrace();
        } catch (Exception e) {
            LOG.error("Error while getting title", e);
            e.printStackTrace();
        }
        return StringUtils.EMPTY;
    }

    @Override
    public Boolean share(Person person, BlogPost blogPost) {
        List<Share> resultList = Share.findSharesByPostAndPerson(blogPost, person);
        if (resultList.isEmpty()) {
            Share share = new Share();
            share.setPerson(person);
            share.setBlogPost(blogPost);
            share.persist();
            return true;
        } else {
            resultList.get(0).remove();
            return false;
        }
    }

    @Override
    public void markRead(Person person, BlogPost blogPost) {
        LOG.debug("mark read");
        ReadShare readShare = new ReadShare();
        readShare.setPerson(person);
        readShare.setBlogPost(blogPost);
        readShare.persist();
    }

}
